﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Text;
using Kimola.Config;

namespace Kimola
{
  /// <summary>
  /// Search result sort types.
  /// </summary>
  public enum KCriteria : int
  {
    /// <summary>
    /// Most related result for the search query shown first.
    /// </summary>
    RelationalDescending = 0,
    /// <summary>
    /// Most related result for the search query shown last.
    /// </summary>
    RelationalAscending = 1,
    /// <summary>
    /// Newest result for the search query shown first.
    /// </summary>
    ChronologicalDescending = 2,
    /// <summary>
    /// Oldest result for the search query shown first.
    /// </summary>
    ChronologicalAscending = 3
  }

  /// <summary>
  /// This class will be used to run all the methods Kimola Cloud Services API provides.
  /// </summary>
  public class KimolaClient
  {
    public KimolaClient()
    {

    }

    public KimolaClient(string apiKey)
    {
      this.apiKey = apiKey;
    }

    private string apiKey;
    private const string defaultPartSpace = "ps";
    private const string defaultCulture = "en";

    /// <summary>
    /// Creates a <see cref="KDocument"/> instance from the settings provided in the config file. <see cref="KDocument"/> class can be instantiated only with this method.
    /// </summary>
    /// <returns>A fresh new <see cref="KDocument"/></returns>
    public KDocument CreateDocument()
    {
      KimolaConfigurationSection kimolaConfigurationSection = (KimolaConfigurationSection)ConfigurationManager.GetSection("kimola");

      KDocument kimolaDocument = new KDocument();
      kimolaDocument.PartSpace = kimolaConfigurationSection == null ? defaultPartSpace : kimolaConfigurationSection.Document.DefaultPartSpace;
      kimolaDocument.Culture = kimolaConfigurationSection == null ? defaultCulture : kimolaConfigurationSection.Document.DefaultCulture;
      kimolaDocument.DocumentDate = DateTime.Now;

      return kimolaDocument;
    }

    /// <summary>
    /// Indexes the specified <see cref="KDocument"/>.
    /// </summary>
    /// <param name="kimolaDocument">The <see cref="KDocument"/> object to index.</param>
    public void Index(KDocument kimolaDocument)
    {
      APIClient apiClient = new APIClient(this.apiKey);
      apiClient.HttpMethod = "POST";
      apiClient.Resource = "documents";
      apiClient.Body = kimolaDocument;
      apiClient.Execute();
    }

    /// <summary>
    /// Indexes up to 10 documents simultaneously thus providing performance gains.
    /// </summary>
    /// <param name="kimolaDocuments">The <see cref="KDocument"/> array.</param>
    public void BatchIndex(KDocument[] kimolaDocuments)
    {
      APIClient apiClient = new APIClient(this.apiKey);
      apiClient.HttpMethod = "PUT";
      apiClient.Resource = "documents";
      apiClient.Body = kimolaDocuments;
      apiClient.Execute();
    }

    /// <summary>
    /// Unindexes a <see cref="KDocument"/> with the document given url.
    /// </summary>
    /// <param name="uri">The URI of the document.</param>
    /// <param name="partSpace">The name of the partspace.</param>
    public void UnIndex(Uri uri, string partSpace)
    {
      APIClient apiClient = new APIClient(this.apiKey);
      apiClient.HttpMethod = "DELETE";
      apiClient.Resource = "documents";
      apiClient.Query.Add("url", uri.AbsoluteUri);
      apiClient.Query.Add("partSpace", partSpace);
      apiClient.Execute();
    }

    /// <summary>
    /// Truncates the whole service. Deletes all the documents, partspaces, suggestions and mappings from the cloud with the given API Key.
    /// </summary>
    public void Truncate()
    {
      APIClient apiClient = new APIClient(this.apiKey);
      apiClient.HttpMethod = "DELETE";
      apiClient.Resource = "documents";
      apiClient.Execute();
    }

    /// <summary>
    /// Gets all the partspaces which belongs to an API Key.
    /// </summary>
    /// <returns>An array of string which contains partspace names for a given API Key.</returns>
    public string[] GetPartSpaces()
    {
      APIClient apiClient = new APIClient(this.apiKey);
      apiClient.HttpMethod = "GET";
      apiClient.Resource = "partspaces";
      return apiClient.Execute<string[]>();
    }

    /// <summary>
    /// Deletes the partspace by the given name.
    /// </summary>
    /// <param name="partSpace">The name of the partspace.</param>
    public void DeletePartSpace(string partSpace)
    {
      APIClient apiClient = new APIClient(this.apiKey);
      apiClient.HttpMethod = "DELETE";
      apiClient.Resource = "documents";
      apiClient.Query.Add("partSpace", partSpace);
      apiClient.Execute();
    }

    /// <summary>
    /// Gets the phrases of a document. These phrases can be used create pre-searced document structure.
    /// </summary>
    /// <param name="uri">The URI of the document.</param>
    /// <returns>An array of string which contains phrases of a document.</returns>
    public string[] GetPhrases(Uri uri)
    {
      APIClient apiClient = new APIClient(this.apiKey);
      apiClient.HttpMethod = "GET";
      apiClient.Resource = "phrases";
      apiClient.Query.Add("url", uri.AbsoluteUri);
      return apiClient.Execute<string[]>();
    }

    /// <summary>
    /// Gets the sugesstions for a given term indexed under an API Key.
    /// </summary>
    /// <param name="term">A term to search for suggestions.</param>
    /// <returns>An array of string which contains suggestions.</returns>
    public string[] GetSugesstions(string term)
    {
      APIClient apiClient = new APIClient(this.apiKey);
      apiClient.HttpMethod = "GET";
      apiClient.Resource = "suggestions";
      apiClient.Query.Add("term", term);
      return apiClient.Execute<string[]>();
    }


    /// <summary>
    /// Performs search and returns a <see cref="KResult"/> for given parameters.
    /// </summary>
    /// <param name="partSpace">A Part Space to search in it. Pass null to search in all part spaces.</param>
    /// <param name="term">Search term.</param>
    /// <param name="criteria">Search criteria which represents sort order.</param>
    /// <param name="pageIndex">Page index for search results to be displayed.</param>
    /// <param name="pageLength">Item count on a single search page.</param>
    /// <returns></returns>
    public KResult Search(string partSpace, string term, KCriteria criteria, int pageIndex, int pageLength)
    {
      APIClient apiClient = new APIClient(this.apiKey);
      apiClient.HttpMethod = "GET";
      apiClient.Resource = "search";
      apiClient.Query.Add("partSpace", partSpace);
      apiClient.Query.Add("term", term);
      apiClient.Query.Add("criteria", Convert.ToByte(criteria).ToString());
      apiClient.Query.Add("pageIndex", pageIndex.ToString());
      apiClient.Query.Add("pageLength", pageLength.ToString());

      return apiClient.Execute<KResult>();
    }

    /// <summary>
    /// Performs search and returns a <see cref="KResult"/> for given parameters.
    /// </summary>
    /// <param name="partSpace">A Part Space to search in it. Pass null to search in all part spaces.</param>
    /// <param name="term">Search term.</param>
    /// <param name="criteria">Search criteria which represents sort order.</param>
    /// <param name="pageIndex">Page index for search results to be displayed.</param>
    /// <param name="pageLength">Item count on a single search page.</param>
    /// <param name="startDate">This value specifies the UTC date range for documents to be searched. If passed null, search will perform from the very begining.</param>
    /// <param name="endDate">This value specifies the UTC date range for documents to be searched. If passed null, search will perform until UTC now.</param>
    /// <returns></returns>
    public KResult Search(string partSpace, string term, KCriteria criteria, int pageIndex, int pageLength, DateTime? startDate, DateTime? endDate)
    {
      APIClient apiClient = new APIClient(this.apiKey);
      apiClient.HttpMethod = "GET";
      apiClient.Resource = "search";
      apiClient.Query.Add("partSpace", partSpace);
      apiClient.Query.Add("term", term);
      apiClient.Query.Add("criteria", Convert.ToByte(criteria).ToString());
      apiClient.Query.Add("pageIndex", pageIndex.ToString());
      apiClient.Query.Add("pageLength", pageLength.ToString());

      if (startDate != (DateTime?)null)
        apiClient.Query.Add("startEpoch", Convert.ToInt64(((DateTime)startDate - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds).ToString());
      if (endDate != (DateTime?)null)
        apiClient.Query.Add("endEpoch", Convert.ToInt64(((DateTime)endDate - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds).ToString());

      return apiClient.Execute<KResult>();
    }
  }
}